<?php
/**
 * IOC容器
 * @author Leo 2010-09-03
 * 
 */
uses ( 'libs.xml' );
interface IIocAware {
	public function afterCreated($ioc);
	public function beforeDespose($ioc);
}
/**
 * IOC容器 
 * @author Leo
 *
 */
class Ioc {
	/**
	 * ioc配置
	 * @var XmlNode
	 */
	private $ioc;
	//需要缓存的实例
	private $instances = array ();
	private static $ins;
	private function __construct($ioc_cfg_file) {
		if (is_file ( $ioc_cfg_file )) {
			$ioc = @file_get_contents ( $ioc_cfg_file );
		} else {
			trigger_error ( '[' . WEBSITE . '] The Ioc Configure file ' . $ioc_cfg_file . ' is not found.', E_USER_ERROR );
		}
		$xml = new XML ();
		$iocroot = $xml->parse ( $ioc );
		$xml->destruct ();
		$xml = null;
		if (is_null ( $iocroot )) {
			trigger_error ( '[' . WEBSITE . '] The Ioc Configure file ' . $ioc_cfg_file . ' is error.', E_USER_ERROR );
		}
		$this->ioc = new XmlNode ( 'beans', $iocroot );
	}
	public function __destruct() {
		$this->destory ();
	}
	/**
	 * 得到IOC容器的一个实例
	 * @param string $ioc_cfg_file IOC容器配置文件
	 * @return Ioc
	 */
	public static function getIoc($ioc_cfg_file = '') {
		$ioc_cfg_file = empty ( $ioc_cfg_file ) ? ETC . 'ioc.xml' : $ioc_cfg_file;
		if (is_null ( self::$ins ) || ! isset ( self::$ins [$ioc_cfg_file] )) {
			self::$ins [$ioc_cfg_file] = new Ioc ( $ioc_cfg_file );
		}
		return self::$ins [$ioc_cfg_file];
	}
	/**
	 * 从IOC容器中得到一个名称为$name的对象实例
	 * @param string $name
	 * @return mixed
	 */
	public function get($name) {
		if (! isset ( $this->instances [$name] )) {
			$bean = $this->ioc->find ( 'bean:id=' . $name . '' );
			if (is_null ( $bean )) {
				return null;
			}
			$class = $bean->attr ( 'class' );
			if (empty ( $class )) {
				return null;
			}
			$class = explode ( '.', $class );
			$clz = array_pop ( $class );
			$class [] = $clz . '.inc';
			$class = join ( '/', $class );
			if (! class_exists ( $clz )) { //如果类还没有加载
				uses ( $class );
			}
			if (class_exists ( $clz )) {
				$params = $bean->find_array ( 'params/param' );
				$args = $this->buildargs ( $params );
				$param_str = join ( ',', $args );
				$exp = "\$clz = new \$clz ({$param_str});";
				eval ( $exp );
				$singleton = $bean->attr ( 'singleton', 'true' );
				$fields = $bean->find_array ( 'fields/field' );
				$this->setFields ( $clz, $fields ); //set 方式注入
				if ('true' === $singleton) { //全局只有一个实例
					$this->instances [$name] = $clz;
				}
				if ($clz instanceof IIocAware) {
					call_user_func_array ( array ($clz, 'afterCreated' ), array ($this ) );
				}
				if ('true' !== $singleton) {
					return $clz;
				}
			} else {
				return null;
			}
		}
		return $this->instances [$name];
	}
	/**
	 * 从IOC容器中得到一个名称为$name的模型实例
	 * @param string $name
	 * @return Model 在容器中配置的模型
	 */
	public function lm($id) {
		return $this->get ( $id );
	}
	/**
	 * 从IOC容器中得到数据源管理器.
	 * @param string $id 数据源管理器ID,默认为dsm
	 * @return DataSourceManager 数据源管理器
	 */
	public function ldsm($id = 'dsm') {
		return $this->get ( $id );
	}
	/**
	 * 从IOC容器中得到数据源
	 * @param string $id
	 * @return DataSource 数据源
	 */
	public function lds($id) {
		return $this->get ( $id );
	}
	/**
	 * 销毁
	 */
	public function destory() {
		foreach ( $this->instances as $key => $obj ) {
			if ($obj instanceof IIocAware) {
				call_user_func_array ( array ($obj, 'beforeDespose' ), array ($this ) );
			}
			$this->instances [$key] = null;
		}
		$this->instances = null;
		$this->ioc = null;
	}
	/**
	 * 构建构造函数的参数
	 * @param array $params
	 * @return array
	 */
	private function buildargs($params) {
		$args = array ();
		foreach ( $params as $param ) {
			$value = $param->attr ( 'value', NULL );
			$ref = $param->attr ( 'ref', NULL );
			$values = $param->find_array ( 'list/value' );
			$items = $param->find_array ( 'array/item' );
			if (! is_null ( $value )) { //具体值				
				$args [] = $value;
			} else if (! is_null ( $ref )) { //引用
				$args [] = '$this->get("' . $ref . '")';
			} else if (! empty ( $values )) { //数组
				$list = array ();
				foreach ( $values as $v ) {
					$key = $v->attr ( 'name' );
					$value = $v->attr ( 'value', NULL );
					$ref = $v->attr ( 'ref', NULL );
					$key = empty ( $key ) ? '' : '"' . $key . '" => ';
					if (! is_null ( $value )) {
						$key .= $value;
					} else if (! is_null ( $ref )) {
						$key .= '$this->get("' . $ref . '")';
					} else {
						$key .= 'NULL';
					}
					$list [] = $key;
				}
				$args [] = 'array(' . join ( ',', $list ) . ')';
			} else if (! empty ( $items )) {
				$list = array ();
				foreach ( $items as $v ) {
					$key = $v->attr ( 'key' );
					$value = $v->attr ( 'value', NULL );
					$ref = $v->attr ( 'ref', NULL );
					$key = empty ( $key ) ? '' : '"' . $key . '" => ';
					if (! is_null ( $value )) {
						$key .= $value;
					} else if (! is_null ( $ref )) {
						$key .= '$this->get("' . $ref . '")';
					} else {
						$key .= 'NULL';
					}
					$list [] = $key;
				}
				$args [] = 'array(' . join ( ',', $list ) . ')';
			}
		}
		return $args;
	}
	/**
	 * 通过set方式注入
	 * @param Object $clz
	 * @param array $fields
	 */
	private function setFields(&$clz, $fields) {
		foreach ( $fields as $field ) {
			$name = $field->name;
			if (is_null ( $name ) || ! method_exists ( $clz, 'set' . ucfirst ( $name ) )) {
				trigger_error ( '[' . WEBSITE . '] The Filed Name is Empty or The Set method for the name is not found.', E_USER_WARNING );
				continue;
			} else {
				$m = 'set' . ucfirst ( $name );
				$value = $field->value;
				$ref = $field->ref;
				$lists = $field->find_array ( 'list/value' );
				$items = $param->find_array ( 'array/item' );
				if (! is_null ( $value )) { //普通值
					call_user_func_array ( array ($clz, $m ), array ($value ) );
				} else if (! is_null ( $ref )) { //引用
					call_user_func_array ( array ($clz, $m ), array ($this->get ( $ref ) ) );
				} else if (! empty ( $lists )) { //数组
					$list = array ();
					foreach ( $lists as $v ) {
						$key = $v->name;
						$value = $v->value;
						$ref = $v->ref;
						if (! is_null ( $ref )) {
							$value = $this->get ( $ref );
						}
						if (! is_null ( $key )) {
							$list [$key] = $value;
						} else {
							$list [] = $value;
						}
					}
					call_user_func_array ( array ($clz, $m ), array ($list ) );
				} else if (! empty ( $items )) {
					$list = array ();
					foreach ( $lists as $v ) {
						$key = $v->key;
						$value = $v->value;
						$ref = $v->ref;
						if (! is_null ( $ref )) {
							$value = $this->get ( $ref );
						}
						if (! is_null ( $key )) {
							$list [$key] = $value;
						} else {
							$list [] = $value;
						}
					}
					call_user_func_array ( array ($clz, $m ), array ($list ) );
				}
			}
		}
	}
}